<?php

/**
 * @author      :   QuangLT
 * @name        :   NewsModel
 * @copyright   :   Fpt Online
 * @todo        :   News model
 */
class News
{
    /**
     * Define how many item will be backup to redis when miss cache 
     */

    const LIMIT_BACKUP        = 500;
    /**
     * Define key cache for news detail
     */
    const NEWS_DETAIL         = 'news_detail';
    /**
     * Define key cache for page extend
     * Page extend page_extend:{article_id}:{page_order}
     */
    const PAGE_EXTEND         = 'page_extend';
    /**
     * Define key cache for Topic detail {topicid}
     */
    const TOPIC_DETAIL        = 'topic_detail';
    /**
     * Define key cache for Tag detail {teamid}
     */
    const TAG_DETAIL          = 'tag_detail';
    /**
     * Define key cache for object reference detail
     */
    const REFERENCE_DETAIL    = 'reference_detail';
    /**
     * Define key cache for list latest news unique cate
     */
    const LIST_NEWS_EACH_CATE = 'list_news_each_cate';

    /**
     * Define key cache max article id 
     */
    const MAX_ARTICLE_ID = 'max_article_id';

    /**
     *
     * @var <type>
     */
    protected static $_instance     = null;

    /**
     * Constructor of class
     * we don't permit an explicit call of the constructor! (like $v = new Singleton())
     */
    protected function __construct()
    {
        
    }

    /**
     * Get singletom instance
     * @return News
     */
    public final static function getInstance()
    {
        //Check instance
        if (is_null(self::$_instance))
        {
            self::$_instance = new self();
        }

        //Return instance
        return self::$_instance;
    }

    /**
     * Function get article detail
     * @param array $articleID
     * @return Array 
     * @author  : phongtx@fpt.net
     */
    public function getDetailArticle($articleID)
    {

        $memcacheInstance = Fpt_Global::getMemcacheInstance($articleID);
        $keyCache         = Fpt_Global::makeCacheKey(self::NEWS_DETAIL, $articleID);

        $arrArticleDetail = $memcacheInstance->read($keyCache);

        // Not in cache?
        if ($arrArticleDetail === FALSE)
        {
            try
            {
                $config    = Fpt_Global::getApplicationIni();
                // Get mysql new instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                $arrArticleDetail = $newsMysql->getDetailArticle($articleID);
                if (!empty($arrArticleDetail))
                {
                    // Store into cache
                    $writeCate = Fpt_Global::writeMemcache($articleID, $keyCache, $arrArticleDetail);
                }
            }
            catch (Zend_Exception $ex)
            {
                Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
            }
        }

        // Return result
        return $arrArticleDetail;
    }

    /**
     * Get list Article By Cate Public Time
     * @author Kietthm
     * @param <array> $arrPamram array('category_id', 'site_id','fromdate','todate')
     * @return <array> List
     */
    public function getArticleByCatePublicTime($arrPamram, $limit, $offset)
    {
        try
        {
            $intTotal         = 0;
            $arrDetailArticle = array();
            $config = Fpt_Global::getApplicationIni();

            $arrPamram['limit']  = $limit;
            $arrPamram['offset'] = $offset;

            // get nosql new instance
            $newsNosql  = Fpt_Business_News::getInstance($config['database']['nosql']);
            $arrResult  = $newsNosql->getArticleByCatePublicTime($arrPamram['category_id'], $arrPamram['todate'], $arrPamram['fromdate'], $offset, $limit);
            $redisTotal = count($arrResult['data']);
            // List hot new not in redis
            if (empty($arrResult['data']) || $redisTotal < $limit)
            {
                // Get mysql new instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                $arrResult = $newsMysql->getArticleByCatePublicTime($arrPamram);
            }
            if (!empty($arrResult) && !empty($arrResult['data']))
            {
                $intTotal = $arrResult['total'];

                // Get article detail
                foreach ($arrResult['data'] as $articleID)
                {
                    // Get detail article
                    $arrDetailArticle[] = $this->getDetailArticle($articleID);
                }
            }
        }
        catch (Zend_Exception $ex)
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return array($arrDetailArticle, $intTotal);
    }

    /**
     * Get object reference by articleID
     * @param int ($articleID,$limit,$offset)
     * @return array
     * @author  : phongtx@fpt.net
     */
    public function getObjectReferenceByArticleID($articleID, $limit, $offset = 0)
    {
        // Result to return
        $arrDetailObjectReferenceByID = array();
        try
        {
            if (intval($articleID) > 0)
            {
                // Get list hot news from redis
                $config          = Fpt_Global::getApplicationIni();
                //get nosql new instance
                $referenceNosql  = Fpt_Business_News::getInstance($config['database']['nosql']);
                // Get list ID of hot news
                $arrReferenceIDs = $referenceNosql->getObjectReferenceByArticleID($articleID, $limit, $offset);
                // List hot new not in redis

                if ($arrReferenceIDs === false || empty($arrReferenceIDs["data"]) || count($arrReferenceIDs["data"]) < $limit)
                {
                    // Get mysql new instance
                    $referenceMysql = Fpt_Business_News::getInstance($config['database']['default']);
                    //get list db
                    $limit          = ($limit > 0) ? $limit : NULL;
                    $arrReferenceDB = $referenceMysql->getObjectReferenceByArticleID($articleID, $limit, $offset);
                    //get db success
                    if (!empty($arrReferenceDB['data']))
                    //loop db return
                        foreach ($arrReferenceDB['data'] as $referenceID => $detail)
                        {
                            $arrReferenceID[] = $referenceID;
                        }
                    unset($arrReferenceDB);
                }
                else
                {
                    $arrReferenceID = $arrReferenceIDs['data'];
                }//end check get list redis fail
                if (!empty($arrReferenceID))
                {
                    // Get detail article by article_id
                    $arrDetailObjectReferenceByID = $this->getObjectReferenceDetailByIDs($arrReferenceID);
                }
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrDetailObjectReferenceByID;
    }

    /**
     * Get object reference detail by array article ids
     * @param   array $arrArticleIDs
     * @return  array $result
     * @author   phongtx@fpt.net
     */
    public function getObjectReferenceDetailByIDs($arrReferenceIDs)
    {
        try
        {
            //default response
            $arrReferenceDetail = false;
            //check valid input
            if (is_array($arrReferenceIDs) && !empty($arrReferenceIDs))
            {

                //loop reference
                foreach ($arrReferenceIDs as $referenceID)
                {
                    //init params
                    $referenceID = intval($referenceID);
                    if ($referenceID > 0)
                    {
                        //get memcache instance sharding by article id
                        $memcacheInstance                 = Fpt_Global::getMemcacheInstance($referenceID);
                        //get key cache
                        $keyCache                         = Fpt_Global::makeCacheKey(self::REFERENCE_DETAIL, $referenceID);
                        //read cache
                        $arrReferenceDetail[$referenceID] = $memcacheInstance->read($keyCache);

                        // Not in cache?
                        if ($arrReferenceDetail[$referenceID] === FALSE)
                        {

                            //push miss cache
                            $arrMissCache[] = $referenceID;
                        }
                    }
                }//end foreach references
                //check is misscache

                if (isset($arrMissCache) && is_array($arrMissCache))
                {
                    $config    = Fpt_Global::getApplicationIni();
                    // Get mysql new instance
                    $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                    //call sp to get detail by array id

                    $arrReferenceDB = $newsMysql->getObjectReferenceDetailByIDs($arrMissCache);

                    //get db success?
                    if (is_array($arrReferenceDB))
                    {
                        foreach ($arrMissCache as $referenceID)
                        {
                            if (!empty($arrReferenceDB[$referenceID]))
                            {

                                $arrReferenceDetail[$referenceID] = $arrReferenceDB[$referenceID];
                            }
                            else
                            {
                                //invalid id
                                $arrReferenceDB[$referenceID] = array();
                            }
                            //get key cache
                            $keyCache = Fpt_Global::makeCacheKey(self::REFERENCE_DETAIL, $referenceID);
                            // Store into cache
                            Fpt_Global::writeMemcache($referenceID, $keyCache, $arrReferenceDetail[$referenceID]);
                        }//end loop misscache
                        unset($arrReferenceDB);
                    }//end check get db ok
                }//end check if miss cache
            }//end check input
        }
        catch (Zend_Exception $ex)
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return $arrReferenceDetail;
    }

    public function getListArticleByCate($categoryID, $limit = 5, $offset = 0, $articleType = NULL)
    {
        // Get application config
        $config    = Fpt_Global::getApplicationIni();
        //get nosql new instance
        $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
        //call redis to get list by time
        $arrReturn = $newsNosql->getListArticleByTime($categoryID, $offset, $limit);
        if ($arrReturn == false || empty($arrReturn["data"]))
        {
            $objcate    = Category::getInstance();
            $listCateId = $objcate->getListCateIdOfParent($categoryID);
            if (!empty($listCateId))
            {
                $categoryID .= ',' . implode(',', $listCateId);
            }
            //init mysql instance
            $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
            //init params get
            if ($categoryID === CATE_ID_PHOTO)
            {
                $articleType = ALBUM;
            }
            else if ($categoryID === CATE_ID_VIDEO)
            {
                $articleType = VIDEO;
            }
            $arrParams   = array(
                'ArticleType' => $articleType,
                'CateID'      => $categoryID,
                'Offset'      => $offset,
                'Limit'       => $limit
            );

            //get db list article by time
            $arrArticleDB = $newsMysql->getListArticleByTime($arrParams);
            // echo '<pre>';  print_r($arrParams);   die();
            if (!empty($arrArticleDB['data']))
            {
                $arrReturn['data'] = array();
                //loop data to push articleID to redisList
                foreach ($arrArticleDB['data'] as $arrArticle)
                {
                    $arrReturn['data'][] = $arrArticle['article_id'];
                }
            }
        }
        return $arrReturn;
    }

    /**
     * get list article by time: only for breakum news, no excluse
     * @param int $categoryID
     * @param int $offset
     * @param int $limit
     * @return mix $arrResponse success?array ID only!!!:false; 
     * @author PhuongTN
     */
    public function getListArticleByTime($categoryID, $limit = 1, $offset = 0, $articleType = NULL, $excluse = NULL)
    {
        try
        {
            //default response
            $arrReturn  = false;
            $categoryID = ($categoryID > 0) ? intval($categoryID) : SITE_ID;
            //Get application config
            $config     = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql  = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn  = $newsNosql->getListArticleByTime($categoryID, $offset, $limit);
            $redisTotal = count($arrReturn["data"]);
            if ($arrReturn == false || empty($arrReturn["data"]) || $redisTotal < $limit)
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                $arrReturn = $newsMysql->getListArticleEachCateByTime($limit);
            }//end check get redis failed?
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    /**
     * get list article by priority
     * @param int $categoryID
     * @param int $offset
     * @param int $limit
     * @return mix $arrResponse success?array ID only!!!:false; 
     * @author PhuongTN
     */
    public function getListTopArticle($categoryID, $limit = 5, $offset = 0, $articleType = NULL, $excluse = NULL)
    {
        try
        {
            //default response
            $arrReturn = false;

            if ($categoryID == SITE_ID)
            {
                return $arrReturn;
            }
            // neu load ajax thi se truyen param excluse
            $arrExcluse = ($excluse == NULL) ? Fpt_Global::getExcluses() : $excluse;
            
            if ($arrExcluse['total'] > 0 && $limit > 0) 
            {
                $originalLimit = $limit;
                //edit offset limit
                $limit+= intval($arrExcluse['total']);
            }

            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getListTopArticle($categoryID, $offset, $limit);

            $redisTotal = count($arrReturn["data"]);

            if ($arrReturn == false || empty($arrReturn["data"]) || $redisTotal < $limit)
            {
                if ($categoryID === CATE_ID_PHOTO)
                {
                    $articleType = ALBUM;
                }
                else if ($categoryID === CATE_ID_VIDEO)
                {
                    $articleType = VIDEO;
                }
                $limit = $limit>0 ? $limit: 500;
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                //init params get
                $arrParams = array(
                    'category_id'  => $categoryID,
                    'article_type' => $articleType,
                    'offset'       => $offset,
                    'limit'        => $limit
                );
                //get db list article by time
                $arrReturn     = $newsMysql->getListTopArticle($arrParams);
            }//end check get redis failed?
            //check if have to excluse 
            if ($arrExcluse['total'] > 0 && count($arrReturn['data']) > 0)
            {
                //get diff articleIDs
                $arrResult            = array_diff($arrReturn['data'], $arrExcluse['data']);
                //then slice to get right limit
                $arrResult            = array_slice($arrResult, 0, $originalLimit);
                //assign return
                $arrReturn['data']    = $arrResult;
                $arrReturn['excluse'] = $arrExcluse;
                unset($arrResult);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    /**
     * get list news on cate(+subcate) order by publish time (no unique cate)
     * @param int $parentCateID
     * @param int $offset
     * @param int $limit
     * @param array $excluse
     * @return boolean 
     * @author PhuongTN
     */
    public function getLatestNewsOnCate($parentCateID, $offset, $limit, $excluse = NULL)
    {
        try
        {
            //default response
            $arrReturn = false;

            if ($parentCateID == SITE_ID)//wrong called
            {
                return $arrReturn;
            }
            
            // neu load ajax thi se truyen param excluse
            $arrExcluse = ($excluse == NULL) ? Fpt_Global::getExcluses() : $excluse;
            
            if ($arrExcluse['total'] > 0 && $limit > 0) 
            {
                $originalLimit = $limit;
                //edit offset limit
                $limit+= intval($arrExcluse['total']);
            }
            
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getLatestNewsOnCate($parentCateID, $offset, $limit);

            $redisTotal = count($arrReturn["data"]);

            if ($arrReturn == false || empty($arrReturn["data"]) || $redisTotal < $limit)
            {
                $modelCate = Category::getInstance();
            
                $arrCateID = $modelCate->getCategoryByParentID($parentCateID);
                $arrCates = array($parentCateID);
                foreach($arrCateID as $cateDetail)
                {
                    $arrCates[] = $cateDetail['category_id'];
                }
                $strCates = implode(',', $arrCates);
                
                $limit = $limit>0 ? $limit: 500;
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
               
                //get db list article by time
                $arrReturn     = $newsMysql->getLatestNewsOnCate($strCates, $offset, $limit);
                unset($arrCateID, $arrCates);
            }//end check get redis failed?
            //check if have to excluse 

            if ($arrExcluse['total'] > 0 && count($arrReturn['data']) > 0)
            {
                //get diff articleIDs
                $arrResult            = array_diff($arrReturn['data'], $arrExcluse['data']);
                //then slice to get right limit
                $arrResult            = array_slice($arrResult, 0, $originalLimit);
                //assign return
                $arrReturn['data']    = $arrResult;
                unset($arrResult);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }
    
    /**
     * get list news younger than this article's publish_time($timeEnd) 
     * order by publish_time desc, no unique cate
     * @param int $categoryID 
     * @param int $timeEnd this article's publish_time
     * @param int $offset
     * @param int $limit
     * @param array $excluse
     * @return boolean
     * @author PhuongTN 
     */
    public function getListOtherNews($categoryID, $timeEnd, $offset, $limit, $excluse = null)
    {
        try
        {
            //default response
            $arrReturn = false;

            if ($categoryID == SITE_ID)//wrong called
            {
                return $arrReturn;
            }
            
            // neu load ajax thi se truyen param excluse
            $arrExcluse = ($excluse == NULL) ? Fpt_Global::getExcluses() : $excluse;
            
            if ($arrExcluse['total'] > 0 && $limit > 0) 
            {
                $originalLimit = $limit;
                //edit offset limit
                $limit+= intval($arrExcluse['total']);
            }
            
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getListOtherNews($categoryID, $timeEnd, $offset, $limit);

            $redisTotal = count($arrReturn["data"]);

            if ($arrReturn == false || empty($arrReturn["data"]) || $redisTotal < $limit)
            {
                $limit = $limit>0 ? $limit: 500;
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
               
                //get db list article by time
                $arrReturn     = $newsMysql->getListOtherNews($categoryID, $timeEnd, $offset, $limit);
                
            }//end check get redis failed?
            //check if have to excluse 

            if ($arrExcluse['total'] > 0 && count($arrReturn['data']) > 0)
            {
                //get diff articleIDs
                $arrResult            = array_diff($arrReturn['data'], $arrExcluse['data']);
                //then slice to get right limit
                $arrResult            = array_slice($arrResult, 0, $originalLimit);
                //assign return
                $arrReturn['data']    = $arrResult;
                unset($arrResult);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }
    
    
    /**
     * Get list $limit article unique cate for box latestnews homepage
     * @param int $limit
     * @return array 
     */
    public function getListArticleEachCateByTime($limit)
    {
        try
        {
            //default response
            $listNews = array();

            //get latest
            $latest = $this->getListArticleByTime(SITE_ID, 1, 0);
            if (isset($latest['data']) && !empty($latest['data']))
            {
                $this->getDetailArticleByIDs(array(intval($latest['data'][0])));
            }
            //get excluse list
            $arrExcluse = Fpt_Global::getExcluses();

            if ($arrExcluse['total'] > 0)
            {
                $originalLimit    = $limit;
                //edit offset limit
                $limit+= intval($arrExcluse['total']);
            }
            //get memcache instance sharding by article id
            $memcacheInstance = Fpt_Global::getMemcacheInstance();
            //get key cache
            $keyCache         = Fpt_Global::makeCacheKey(self::LIST_NEWS_EACH_CATE);
            //read cache
            $listNews         = $memcacheInstance->read($keyCache);

            if ($listNews === false || count($listNews) < $limit)
            {
                $config    = Fpt_Global::getApplicationIni();
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                $listNews  = $newsMysql->getListArticleEachCateByTime($limit);
                if (!empty($listNews['data']))
                {
                    Fpt_Global::writeMemcache(0, $keyCache, $listNews['data']);
                }
            }

            //check if have to excluse 
            if ($arrExcluse['total'] > 0 && count($listNews) > 0)
            {
                //get diff articleIDs
                $arrResult = array_diff($listNews, $arrExcluse['data']);
                //then slice to get right limit
                $arrResult = array_slice($arrResult, 0, $originalLimit);
                //assign return
                $listNews  = $arrResult;
                //unset data
                unset($arrResult);
            }
        }
        catch (Zend_Exception $ex)
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return $listNews;
    }

    /**
     * get list article by Topic
     * @param int $topicID
     * @param int $limit
     * @param int $offset
     * @return array 
     */
    public function getListArticleByTopic($topicID, $limit, $offset = 0)
    {
        try
        {
            //default response
            $arrReturn = array();
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getListArticleByTopic($topicID, $offset, $limit);

            if ($arrReturn == false || count($arrReturn["data"]) < $limit)
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                $limit     = ($limit > 0) ? $limit : NULL;
                //init params get
                $arrParams = array(
                    'ArticleType' => NULL,
                    'TopicID'     => $topicID,
                    'Offset'      => $offset,
                    'Limit'       => $limit
                );

                //get db list article by time
                $arrArticleDB = $newsMysql->getListArticleByTopic($arrParams);
                //check db
                if ($arrArticleDB != false)
                {
                    //assign total param
                    $arrReturn['total'] = $arrArticleDB['total'];

                    if (!empty($arrArticleDB['data']))
                    {
                        $arrReturn['data'] = array();
                        //loop data to push articleID to redisList
                        foreach ($arrArticleDB['data'] as $arrArticle)
                        {
                            $arrReturn['data'][] = $arrArticle['article_id'];
                        }
                    }
                    unset($arrArticleDB);
                }//end check get db ok
            }//end check get redis failed?
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    /**
     * get list article by Tag
     * @param int $tagID
     * @param int $limit
     * @param int $offset
     * @return array
     */
    public function getListArticleByTag($tagID, $limit, $offset = 0, $articleType = NULL)
    {
        try
        {
            //default response
            $arrReturn = array();
            $tagID = intval($tagID);
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getListArticleByTag($tagID, $offset, $limit, $articleType);

            if ($arrReturn == false || empty($arrReturn["data"]) || count($arrReturn["data"]) < $limit)
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                //init params get
                $arrParams = array(
                    'ArticleType' => NULL,
                    'TagID'       => $tagID,
                    'Offset'      => $offset,
                    'Limit'       => $limit,
                    'ArticleType' => $articleType
                );

                //get db list article by time
                $arrArticleDB = $newsMysql->getListArticleByTag($arrParams);
                //check db
                if ($arrArticleDB != false)
                {
                    //assign total param
                    $arrReturn['total'] = $arrArticleDB['total'];

                    if (!empty($arrArticleDB['data']))
                    {
                        $arrReturn['data'] = array();
                        //loop data to push articleID to redisList
                        foreach ($arrArticleDB['data'] as $arrArticle)
                        {
                            $arrReturn['data'][] = $arrArticle['article_id'];
                        }
                    }
                    unset($arrArticleDB);
                }//end check get db ok
            }//end check get redis failed?
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    public function getListArticleByHotNotExcluse($arrParams)//$categoryID, $limit, $siteID, $offset = 0)
    {
        try
        {
            $arrParams['category_id'] = (isset($arrParams['category_id']) && $arrParams['category_id'] > 0) ? intval($arrParams['category_id']) : SITE_ID;
            // Get application config
            $config                   = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql                = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by hot
            $arrReturn                = $newsNosql->getListArticleByHot($arrParams);

            if ($arrReturn == false || empty($arrReturn["data"]))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                if ($arrParams['category_id'] == SITE_ID)
                {
                    //get db list article by hot
                    $arrArticleDB = $newsMysql->getListArticleByHot($arrParams);
                }
                else
                {
                    $arrArticleDB = $newsMysql->getListArticleByHotOnCate($arrParams);
                }

                if (!empty($arrArticleDB['data']))
                {
                    $arrReturn['data'] = array();
                    //loop data to push articleID to redisList
                    foreach ($arrArticleDB['data'] as $arrArticle)
                    {
                        $arrReturn['data'][] = $arrArticle['article_id'];
                    }
                }
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    /**
     * Get list article by hot
     * @param array $arrParams (category_id,article_type,limit,offset,site_id)
     * @return array $arrReturn
     * @author  : phongtx@fpt.net
     */
    public function getListArticleByHot($arrParams)
    {//$categoryID, $limit, $siteID, $offset = 0) 
        try
        {
            //default response
            $arrReturn = false;

            $arrParams['category_id'] = (isset($arrParams['category_id']) && $arrParams['category_id'] > 0) ? intval($arrParams['category_id']) : SITE_ID;
            //get excluse list
            $arrExcluse               = Fpt_Global::getExcluses();

            if ($arrExcluse['total'] > 0)
            {
                $originalLimit = $arrParams['limit'];
                //edit offset limit
                $arrParams['limit']+= intval($arrExcluse['total']);
            }
            // Get application config
            $config        = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql     = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by hot
            $arrReturn     = $newsNosql->getListArticleByHot($arrParams);

            if ($arrReturn == false || empty($arrReturn["data"]) || count($arrReturn["data"]) < $arrParams['limit'])
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                if ($arrParams['category_id'] == SITE_ID)
                {

                    //get db list article by hot
                    $arrArticleDB = $newsMysql->getListArticleByHot($arrParams);
                }
                else
                {
                    $arrArticleDB = $newsMysql->getListArticleByHotOnCate($arrParams);
                }

                //check db
                if ($arrArticleDB != false)
                {
                    //assign total param
                    $arrReturn['total'] = $arrArticleDB['total'];

                    if (!empty($arrArticleDB['data']))
                    {
                        $arrReturn['data'] = array();
                        //loop data to push articleID to redisList
                        foreach ($arrArticleDB['data'] as $arrArticle)
                        {
                            $arrReturn['data'][] = $arrArticle['article_id'];
                        }
                    }
                    unset($arrArticleDB);
                }//end check get db ok
            }//end check get redis failed?
            //check if have to excluse 
            if ($arrExcluse['total'] > 0 && count($arrReturn['data']) > 0)
            {
                //get diff articleIDs
                $arrResult         = array_diff($arrReturn['data'], $arrExcluse['data']);
                //then slice to get right limit
                $arrResult         = array_slice($arrResult, 0, $originalLimit);
                //assign return
                $arrReturn['data'] = $arrResult;
                //unset data
                unset($arrResult);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    /**
     * get list article by team
     * @param int $teamID
     * @param int $offset
     * @param int $limit
     * @return array
     * @author TriNT
     */
    public function getListArticleByTeam($teamID, $limit, $offset = 0)
    {
        try
        {
            //default response
            $arrReturn = false;
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $arrReturn = $newsNosql->getListArticleByTeam($teamID, $offset, $limit);

            if ($arrReturn == false || empty($arrReturn["data"]) || count($arrReturn["data"]) < $limit)
            {
                //init mysql instance
                $newsMysql    = Fpt_Business_News::getInstance($config['database']['default']);
                //init params get
                //get db list article by time
                $arrArticleDB = $newsMysql->getListArticleByTeam($teamID, $limit, $offset);
                //check db
                if ($arrArticleDB != false)
                {
                    //assign total param
                    $arrReturn['total'] = $arrArticleDB['total'];

                    if (!empty($arrArticleDB['data']))
                    {
                        $arrReturn['data'] = array();
                        //loop data to push articleID to redisList
                        foreach ($arrArticleDB['data'] as $arrArticle)
                        {
                            $arrReturn['data'][] = $arrArticle['article_id'];
                        }
                    }
                    unset($arrArticleDB);
                }//end check get db ok
            }//end check get redis failed?
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrReturn;
    }

    //START BACKEND FUNCTION 

    /**
     * set hot article
     * @param int $articleID
     * @return array 
     * @author PhuongTN
     */
    public function setHotArticle($articleID)
    {
        try
        {
            $response = array();
            //get hot info
            $hotInfo = $this->getHotInfoByArticleID($articleID);

            if (!empty($hotInfo))
            {

                //get application conf
                $config    = Fpt_Global::getApplicationIni();
                //init nosql instance
                $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
                //set hot article
                $response  = $newsNosql->setHotArticle($hotInfo);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * unset hot article
     * @param int $articleID
     * @param int $categoryID
     * @return array 
     */
    public function unsetHotArticle($articleID, $categoryID)
    {
        try
        {
            $response = array();
            //get application conf
            $config    = Fpt_Global::getApplicationIni();
            //init nosql instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //unset hot article
            $response  = $newsNosql->unsetHotArticle($articleID, $categoryID);
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * Update all position in cateID
     * @param type $cateID
     * @return type 
     * @author PhuongTN
     */
    public function updateHotByCate($cateID)
    {
        try
        {
            $response = array();
            $arrParams = array(
                'category_id'  => $cateID,
                'article_type' => NULL,
                'limit'        => 100,
                'offset'       => 0,
                'site_id'      => SITE_ID
            );
            //get application conf
            $config        = Fpt_Global::getApplicationIni();
            //init nosql instance
            $newsSql       = Fpt_Business_News::getInstance($config['database']['default']);
            //set hot article
            $response      = $newsSql->getListArticleByHot($arrParams);

            if (!empty($response['data']))
            {
                //loop data to push articleID to redisList
                foreach ($response['data'] as $arrArticle)
                {
                    $arrIDs[]  = $arrArticle['article_id'];
                }
                $newsNoSql = Fpt_Business_News::getInstance($config['database']['nosql']);
                $response  = $newsNoSql->updateHotByCate($cateID, $arrIDs);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * Add bai viet vao topic
     * @param int $topicID
     * @param array $articleIDs
     * @return array $response 
     */
    public function addTopicArticles($topicID, $articleIDs)
    {
        $response = array();
        try
        {
            $config     = Fpt_Global::getApplicationIni();
            //getdetail article
            $arrDetails = $this->getDetailArticleByIDs($articleIDs);

            if (!empty($arrDetails))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                foreach ($arrDetails as $articleID => $detail)
                {
                    //get db list topic of article
                    $topicConfig = $newsMysql->getTopicsByArticleID($articleID, $topicID);
                    if (is_array($topicConfig) && !empty($topicConfig))
                    {
                        $arrData[$articleID]['topic_config'] = $topicConfig[0];
                    }

                    $arrData[$articleID]['public_time'] = $detail['public_time'];
                }
                unset($arrDetails);

                //init nosql instance
                $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
                //set hot article
                $response  = $newsNosql->addTopicArticles($topicID, $arrData);
            }
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * del bai viet vao topic
     * @param int $topicID
     * @param array $articleIDs
     * @return array $response 
     */
    public function deleteTopicArticles($topicID, $articleIDs)
    {
        $response = array();
        try
        {
            $config     = Fpt_Global::getApplicationIni();
            //getdetail article
            $arrDetails = $this->getDetailArticleByIDs($articleIDs);

            if (!empty($arrDetails))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                foreach ($arrDetails as $articleID => $detail)
                {
                    //get db list topic of article
                    $topicConfig = $newsMysql->getTopicsByArticleID($articleID);

                    if (is_array($topicConfig) && !empty($topicConfig))
                    {
                        $arrData[$articleID]['topic_config'] = $topicConfig;
                    }

                    $arrData[$articleID]['public_time'] = $detail['public_time'];
                }
                unset($arrDetails);

                //init nosql instance
                $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
                //set hot article
                $response  = $newsNosql->deleteTopicArticles($topicID, $arrData);
            }
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * BACKEND: edit topic => clear cache cate, dont' del data
     * @param int $topicID
     * @return type 
     */
    public function editTopic($topicID)
    {
        try
        {
            $delRS    = false;
            //get key cache
            $keyCache = Fpt_Global::makeCacheKey(self::TOPIC_DETAIL, $topicID);

            //Delete 1 key cache full only
            $delRS = Fpt_Global::deleteMemcache($topicID, $keyCache);

            //precaching
            $this->getDetailTopic(array($topicID));
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $delRS;
    }

    /**
     * delete news by topic
     * @param type $topicID
     * @return bool 
     * @author PhuongTN
     */
    public function deleteArticleByTopicID($topicID)
    {
        //default return
        $response = false;
        try
        {
            //get list article of this topic
            $arrArticle = $this->getListArticleByTopic($topicID, 0, 0);
            $arrDel = array();
            //is empty?
            if (is_array($arrArticle))
            {
                foreach ($arrArticle as $articleID)
                {
                    $arrDel[$articleID] = array();
                    
                }
                //del article
                //get application conf
                $config    = Fpt_Global::getApplicationIni();
                //get nosql new instance
                $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
                $response[] = $newsNosql->deleteTopicArticles($topicID, $arrDel, true);
            }
            //get key cache
            $keyCache   = Fpt_Global::makeCacheKey(self::TOPIC_DETAIL, $topicID);

            $response[] = Fpt_Global::deleteMemcache($topicID, $keyCache);

            return $response;
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * get all topic of 1 articleID 
     * This func is only use for backend job: JobAsync!
     * @param int $articleID 
     * @return array $response
     * @author PhuongTN
     */
    public function getTopicsByArticleID($articleID)
    {
        try
        {

            $response = array();
            //get application conf
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $response  = $newsNosql->getTopicsByArticleID($articleID);
            if (empty($response))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                //get db list topic of article
                $response  = $newsMysql->getTopicsByArticleID($articleID);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * get categories related to this aricle id
     * @param int $articleID
     * @return array 
     * @author PhuongTN
     */
    public function getCategoriesByArticleID($articleID)
    {
        try
        {
            $response = array();
            //get application conf
            $config = Fpt_Global::getApplicationIni();

            $newsNoSql = Fpt_Business_News::getInstance($config['database']['nosql']);

            $response = $newsNoSql->getCategoriesByArticleID($articleID);
            if (empty($response))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
                //get db list cate
                $response  = $newsMysql->getCategoriesByArticleID($articleID);

                if (is_array($response) && count($response) > 0)
                {
                    $arrCateIDs = array();
                    foreach ($response as $cateRS)
                    {
                        $arrCateIDs[] = intval($cateRS['category_id']);
                    }
                    $arrCateIDs   = array_unique($arrCateIDs);
                    return $arrCateIDs;
                }
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * get categories related to this aricle id
     * @param int $articleID
     * @return array 
     * @author PhuongTN
     */
    public function getTagsByArticleID($articleID)
    {
        try
        {
            $response = array();

            //get application conf
            $config    = Fpt_Global::getApplicationIni();
            //init redis instance
            $newsNoSql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //get tag list
            $response  = $newsNoSql->getTagsByArticleID($articleID);

            if (empty($response))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                //get db list cate
                $response = $newsMysql->getTags($articleID);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * get teams of article
     * @param int $articleID
     * @return array
     * @author PhuongTN 
     */
    public function getTeamsByArticleID($articleID)
    {
        try
        {
            $response = array();
            //get application conf
            $config    = Fpt_Global::getApplicationIni();
            //init mysql instance
            $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
            //get db list cate
            $response  = $newsMysql->getTeamsByArticleID($articleID);

            if (is_array($response) && count($response) > 0)
            {
                $arrTeamIDs = array();
                foreach ($response as $teamRS)
                {
                    $arrTeamIDs[] = intval($teamRS['team_id']);
                }
                $arrTeamIDs   = array_unique($arrTeamIDs);
                return $arrTeamIDs;
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * Get list other Article By Cate Create Time
     * @author PhongTX
     * @param <array> $arrPamram
     * @return <array> List
     */
    public function getListOtherArticleByCateCreateTime($arrPamram)
    {
        $arrArticleIds = array();
        try 
        {
            // Get config
            $config = Fpt_Global::getApplicationIni();

            // get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            $arrArticleIds = $newsNosql->getArticleByCatePublicTime($arrPamram['category_id'],$arrPamram['todate'],$arrPamram['fromdate'],$arrPamram['offset'],$arrPamram['limit']);
            // List hot new not in redis
            if (empty($arrArticleIds['data'])) 
            {
                $intTotal = 0;
                $config = Fpt_Global::getApplicationIni();
                // Get mysql new instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                $arrResult = $newsMysql->getArticleByCatePublicTime($arrPamram);
                $arrArticleIds['data'] = array();
                if (!empty($arrResult))
                {
                    $intTotal = $arrResult['total'];

                    if(!empty($arrResult['data']))
                    {
                        // Get list article id
                        foreach ($arrResult['data'] as $article_id) 
                        {
                            $arrArticleIds['data'][] = $article_id;
                        }
                        
                    }
                }
            }
            if(is_array($arrArticleIds['data']))
            {
                $arrArticleIds['data'] = array_diff($arrArticleIds['data'], $arrPamram['intArticleId']);
            }
        } 
        catch (Zend_Exception $ex) 
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return $arrArticleIds;
    }

    /**
     * get hot info
     * @param int $articleID
     * @return array
     * @author PhuongTN 
     */
    public function getHotInfoByArticleID($articleID)
    {
        try
        {
            $response = array();
            //get application conf
            $config    = Fpt_Global::getApplicationIni();
            $newsNoSql = Fpt_Business_News::getInstance($config['database']['nosql']);
            $response  = $newsNoSql->getHotInfoByArticleID($articleID);
            if (empty($response))
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                //get db list cate
                $response = $newsMysql->getHotInfoByArticleID($articleID);
            }
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * Function get article detail by array id
     * @param array $articleIDs
     * @return Array 
     * @author  : phongtx@fpt.net
     * @modify: PhuongTN
     */
    public function getDetailArticleByIDs($articleIDs)
    {
        try
        {
            //default response
            $arrArticleDetail = array();
            //check valid input
            if (is_array($articleIDs))
            {
                //loop article
                foreach ($articleIDs as $articleID)
                {
                    //init params
                    $articleID = intval($articleID);
                    if ($articleID > 0)
                    {
                        $maxID          = $this->getMaxArticle();
                      
                        //if miss cache max id || id < max valid id
                        if ($maxID === false || $articleID <= $maxID)
                        {
                            //get memcache instance sharding by article id
                            $memcacheInstance = Fpt_Global::getMemcacheInstance($articleID);
                            //get key cache
                            $keyCache         = Fpt_Global::makeCacheKey(self::NEWS_DETAIL, $articleID);
                            //read cache
                            $articleDetail    = $memcacheInstance->read($keyCache);
                            // Not in cache?
                            if ($articleDetail === FALSE)
                            {
                                //push miss cache
                                $arrMissCache[] = $articleID;
                            }
                            elseif (!empty($articleDetail))
                            {
                                $arrArticleDetail[$articleID] = $articleDetail;
                            }
                        }
                    }
                }//end foreach articles
                //check is misscache
                if (isset($arrMissCache) && is_array($arrMissCache))
                {
                    $config       = Fpt_Global::getApplicationIni();
                    // Get mysql new instance
                    $newsMysql    = Fpt_Business_News::getInstance($config['database']['default']);
                    //call sp to get detail by array id
                    $arrArticleDB = $newsMysql->getDetailArticleByIDs($arrMissCache);
                    //get db success?
                    if (is_array($arrArticleDB))
                    {
                        foreach ($arrMissCache as $articleID)
                        {
                            if (!empty($arrArticleDB[$articleID]))
                            {

                                $arrArticleDetail[$articleID] = $arrArticleDB[$articleID];
                            }
                            else
                            {
                                //invalid id
                                $arrArticleDetail[$articleID] = array();
                            }

                            //get key cache
                            $keyCache = Fpt_Global::makeCacheKey(self::NEWS_DETAIL, $articleID);
                            // Store into cache
                            Fpt_Global::writeMemcache($articleID, $keyCache, $arrArticleDetail[$articleID]);
                        }//end loop misscache
                    }//end check get db ok
                }//end check if miss cache
            }//end check input
            if (!empty($arrArticleDetail))
            {
                Fpt_Global::setExcluses(array_keys($arrArticleDetail));
            }
        }
        catch (Zend_Exception $ex)
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return $arrArticleDetail;
    }

    /**
     * add article job
     * @param array $data array(articleID,categories,teamIDs,topicIDs,publicDate,hotOrder)
     *              categories, teamIDs, topicIDs are array
     * @param array $detail
     * @return boolean 
     */
    public function addArticle($articleID, $cachefile = true)
    {
        //default return
        $response = false;
        try
        {
            $articleID = intval($articleID);
            if ($articleID > 0)
            {
                $maxID          = $this->getMaxArticle();
                //get config
                $config     = Fpt_Global::getApplicationIni();
                //get redis instance
                $modelRedis = Fpt_Business_News::getInstance($config['database']['nosql']);
                if ($articleID > $maxID)
                {
                    $modelRedis->setRangeArticle($articleID);
                }
                //get article detail
                $result = $this->getDetailArticleByIDs(array($articleID));
                //check result
                if (is_array($result) && !empty($result[$articleID]))
                {
                    $detail     = $result[$articleID];
                    

                    //check publish_time
                    if ($detail['publish_time'] > time())
                    {
//                        echo $detail['article_id'];
//                        echo '  with:' . $detail['publish_time'] . '   ' . $detail['public_time'];
                        return $modelRedis->addScheduleArticle($detail);
                    }

                    //get key cache
                    $keyCache = Fpt_Global::makeCacheKey(self::NEWS_DETAIL, $articleID);
                    //write memcache news details
                    Fpt_Global::writeMemcache($articleID, $keyCache, $detail);
                    //del cache latest unique cateid
                    $keyCache = Fpt_Global::makeCacheKey(self::LIST_NEWS_EACH_CATE);
                    Fpt_Global::deleteMemcache(0, $keyCache);
                    
                    //precaching page_extend
                    $totalPage = intval($detail['total_page']);
                    if ($totalPage > 0)
                    {
                        for ($i = 0; $i < $totalPage; $i++)
                        {
                            //precaching
                            $this->getDetailPageExtend($articleID, $i);
                        }
                    }
                    //get list topic
                    $arrTopic           = $this->getTopicsByArticleID($articleID);
                    //get list cate
                    $arrCate            = $this->getCategoriesByArticleID($articleID);
                    //get list tag
                    $arrTag             = $this->getTagsByArticleID($articleID);
                    //get list hot info: what cates & order?
                    $arrHot             = $this->getHotInfoByArticleID($articleID);
 
                    $arrCateDetail = array();
                    
                    if(!empty($arrCate) && $detail['show_status'] == 1)
                    {
                        //old article lost orginal_cate on table article_cate
                        $arrCate[] = $detail['original_cate'];
                        //for original case
                        $arrCate = array_unique($arrCate);
                        //new cate model
                        $cateModel = Category::getInstance();
                        //get cates detail
                        foreach ($arrCate as $cateID) 
                        {
                            $detailCate = $cateModel->getDetailbyCateId($cateID);
                            if(!empty($detailCate))
                            {
                                $arrCateDetail[$cateID]['full_parent'] = $detailCate['full_parent'];
                            }
                        }
                        unset($arrCate);
                    }
                    
                    //assign data
                    $detail['categories']      = $arrCateDetail;
                    $detail['tagIDs']          = $arrTag;
                    $detail['topicIDs']        = $arrTopic;
                    $detail['hotInfo']         = $arrHot;
                    $detail['objectReference'] = !empty($detail['list_reference'])?explode(',', $detail['list_reference']):array();

                    //call redis add article
                    $response = $modelRedis->addArticle($detail);
                    
                    if ($cachefile !== false)
                    {
                        //Create job client
                        $jobClient    = Fpt_Global::getJobClientInstance('sport');
                        //get config
                        $configLink   = $config['cache']['base_url'];
                        $configCache  = $config['cache']['dir'];
                        //generate link
                        define(BASE_URL, $configLink);
                        //link 
                        $articleLink  = BASE_URL . Fpt_Global::getArticleUrl($detail);
                        //storage location
                        $articleCache = str_replace($configLink, $configCache, $articleLink);
                        //init array params
                        $arrParams    = array('add', $articleLink, $articleCache);
                        //implode to generate job params
                        $jobParams = implode(GEARMAN_SEPERATOR, $arrParams);
                        //Register job
                        $rs        = $jobClient->doBackgroundTask(Fpt_Global::getJobFunction('sport', 'sync_cache'), $jobParams);
                    }
                    else
                    {
						//for monitor writecache
                        echo '==='.$articleID;
                    }
                    return $response;
                }
            }
            return false;
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * delete article job
     * @param array $oldData
     * @param array $data array(articleID,categories,teamIDs,topicIDs,publicDate,hotOrder)
     *              categories, teamIDs, topicIDs are array
     * @return boolean 
     * @author PhuongTN
     */
    public function deleteArticle($articleID, $detail = array(), $isUpdate = false)
    {
        //default return
        $response = false;
        try
        {
            $articleID = intval($articleID);
            if ($articleID > 0)
            {
                if (empty($detail) && $isUpdate == true)
                {
                    //get article detail: currently we can't delete or change status article
                    $result = $this->getDetailArticleByIDs(array($articleID));
                    if (is_array($result) && !empty($result[$articleID]))
                    {
                        $detail = $result[$articleID];
                    }
                }
                if (!empty($detail))
                {
                    //get key cache
                    $keyCache = Fpt_Global::makeCacheKey(self::NEWS_DETAIL, $articleID);
                    //del memcache news details
                    Fpt_Global::deleteMemcache($articleID, $keyCache);

                    //delete cache page_extend
                    $totalPage = intval($detail['total_page']);
                    if ($totalPage > 0)
                    {
                        for ($i = 1; $i <= $totalPage; $i++)
                        {
                            //get key cache
                            $keyCache = Fpt_Global::makeCacheKey(self::PAGE_EXTEND, array($articleID, $i));
                            //del memcache news details
                            Fpt_Global::deleteMemcache($articleID, $keyCache);
                        }
                    }
                    //get list topic redis
                    $arrTopic = $this->getTopicsByArticleID($articleID);
                    //get list cate 
                    $arrCate  = $this->getCategoriesByArticleID($articleID);
                    //get list tag
                    $arrTag   = $this->getTagsByArticleID($articleID);

                    $config         = Fpt_Global::getApplicationIni();
                    // Get mysql new instance
                    $referenceMysql = Fpt_Business_News::getInstance($config['database']['default']);
                    //get list db
                    $arrReferenceDB = $referenceMysql->getObjectReferenceByArticleID($articleID, NULL, 0);
                    
                    if (is_array($arrReferenceDB) && !empty($arrReferenceDB['data']))
                    {
                        foreach ($arrReferenceDB['data'] as $referenceID => $detail)
                        {
                            //get key cache
                            $keyCache       = Fpt_Global::makeCacheKey(self::REFERENCE_DETAIL, $referenceID);
                            //read cache
                            Fpt_Global::deleteMemcache($referenceID, $keyCache);
                        }
                    }
                    $modelStatistic = Statictis::getInstance();
                    $modelStatistic->syncStatistic();

                    //get list hot info: what cates & order?
                    $arrHot = $this->getHotInfoByArticleID($articleID);

                    $arrCateDetail = array();
                    
                    if(!empty($arrCate))
                    {
                        //old article lost orginal_cate on table article_cate
                        $arrCate[] = $detail['original_cate'];
                        //for original case
                        $arrCate = array_unique($arrCate);
                        //new cate model
                        $cateModel = Category::getInstance();
                        //get cates detail
                        foreach ($arrCate as $cateID) 
                        {
                            $detailCate = $cateModel->getDetailbyCateId($cateID);
                            if(!empty($detailCate))
                            {
                                $arrCateDetail[$cateID]['full_parent'] = $detailCate['full_parent'];
                            }
                        }
                        unset($arrCate);
                    }
                    //assign data
                    $detail['categories']      = $arrCateDetail;
                    $detail['tagIDs']          = $arrTag;
                    $detail['topicIDs']        = $arrTopic;
                    $detail['hotInfo']         = $arrHot;
                    $detail['objectReference'] = $arrObjectReference;

                    //get config
                    $config       = Fpt_Global::getApplicationIni();
                    //get redis instance
                    $modelRedis   = Fpt_Business_News::getInstance($config['database']['nosql']);
                    //call redis add article
                    $response     = $modelRedis->deleteArticle($detail);
                    //Create job client
                    $jobClient    = Fpt_Global::getJobClientInstance('sport');
                    //get config
                    $configLink   = $config['cache']['base_url'];
                    $configCache  = $config['cache']['dir'];
                    //generate link
                    define(BASE_URL, $configLink);
                    //link 
                    $articleLink  = Fpt_Global::getArticleUrl($detail);
                    //storage location
                    $articleCache = str_replace($configLink, $configCache, $articleLink);
                    //init array params
                    $arrParams    = array('delete', $articleLink, $articleCache);
                    //implode to generate job params
                    $jobParams = implode(GEARMAN_SEPERATOR, $arrParams);
                    //Register job
                    $jobClient->doBackgroundTask(Fpt_Global::getJobFunction('sport', 'sync_cache'), $jobParams);
                    
                    return $response;
                }
                $response  = Fpt_Server::setOutputData(-1, 'Invalid param', null);
            }
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * delete news by category
     * @param int $cateID
     * @return bool 
     * @author PhuongTN
     */
    public function deleteArticleByCateID($cateID)
    {
        //default return
        $response = false;
        try
        {
            //get list article of this cateID
            $arrArticle = $this->getListTopArticle($cateID, 0, 0);
            //is empty?
            if (is_array($arrArticle))
            {
                foreach ($arrArticle as $articleID)
                {
                    //del article
                    $response[] = $this->deleteArticle($articleID);
                }
            }
            return $response;
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * delete news by team
     * @param int $teamID
     * @return bool 
     * @author PhuongTN
     */
    public function deleteArticleByTeamID($teamID)
    {
        //default return
        $response = false;
        try
        {
            //get list article of this team
            $arrArticle = $this->getListArticleByTeam($teamID, 0, 0);
            //is empty?
            if (is_array($arrArticle))
            {
                foreach ($arrArticle as $articleID)
                {
                    //del article
                    $response[] = $this->deleteArticle($articleID);
                }
            }
            //get key cache
            $keyCache   = Fpt_Global::makeCacheKey(self::TAG_DETAIL, $teamID);
            //Delete 1 key cache full only
            $response[] = Fpt_Global::deleteMemcache($teamID, $keyCache);
            return $response;
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    /**
     * Function get tags of artilce
     * @param string One or more tagid, seperate by comma
     * @return array
     * @author  : nhanva@fpt.net
     */
    public function getTagByID($strTagID)
    {
        // Get tags from Memcache
        $arrTags = array();
        try
        {
            $arrTagIDs = explode(',', $strTagID);

            if (!empty($arrTagIDs))
            {
                foreach ($arrTagIDs as $tagID)
                {
                    $memcacheInstance = Fpt_Global::getMemcacheInstance();
                    $keyCache         = Fpt_Global::makeCacheKey(self::TAG_DETAIL, $tagID);
                    $detail           = $memcacheInstance->read($keyCache);

                    if ($detail !== false)
                    {
                        $arrTags[$tagID] = $detail;
                    }
                    else
                    {
                        $arrMissCache[] = $tagID;
                    }
                }
                //check is misscache
                if (isset($arrMissCache) && is_array($arrMissCache))
                {
                    $config       = Fpt_Global::getApplicationIni();
                    // Get mysql new instance
                    $newsMysql    = Fpt_Business_News::getInstance($config['database']['default']);
                    $strMissCache = implode(',', $arrMissCache);
                    //call sp to get detail by array id
                    $arrTagDB     = $newsMysql->getTagByID($strMissCache);
                    //get db success?
                    if (!empty($arrTagDB))
                    {
                        foreach ($arrMissCache as $tagID)
                        {
                            if (!empty($arrTagDB[$tagID]))
                            {
                                $arrTags[$tagID] = $arrTagDB[$tagID];
                            }
                            else
                            {
                                //invalid id
                                $arrTagDB[$tagID] = array();
                            }

                            //get key cache
                            $keyCache = Fpt_Global::makeCacheKey(self::TAG_DETAIL, $tagID);
                            // Store into cache
                            Fpt_Global::writeMemcache($tagID, $keyCache, $arrTags[$tagID]);
                        }//end loop misscache
                    }//end check get db ok
                }//end check if miss cache
            }
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $arrTags;
    }

    /**
     * Get topic detail
     * @param <array> $arrTopicID list topic id
     */
    public function getDetailTopic($arrTopicID = array())
    {
        $arrReturn = array();
        $arrMissCache = array();
        foreach ($arrTopicID as $intTopicID)
        {
            //get memcache instance sharding by article id
            $memcacheInstance = Fpt_Global::getMemcacheInstance($intTopicID);
            //get key cache
            $keyCache         = Fpt_Global::makeCacheKey(self::TOPIC_DETAIL, $intTopicID);

            // Read data in cache
            $arrTopicDetail = $memcacheInstance->read($keyCache);
            if (!empty($arrTopicDetail))
            {
                $arrReturn[$intTopicID] = $arrTopicDetail;
            }
            else
            {
                $arrMissCache[] = $intTopicID;
            }
        }

        // Check miss cache
        if (!empty($arrMissCache))
        {
            // Get topic in DB
            $config    = Fpt_Global::getApplicationIni();
            // Get mysql new instance
            $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

            // Get miss key in DB
            $arrTopicDB = $newsMysql->getDetailTopic(join(',', $arrMissCache));
            foreach ($arrTopicDB as $arrTopic)
            {
                $intTopicID             = $arrTopic['topic_id'];
                $arrReturn[$intTopicID] = $arrTopic;

                // Build key cache
                $keyCache = Fpt_Global::makeCacheKey(self::TOPIC_DETAIL, $intTopicID);

                // Store into cache
                Fpt_Global::writeMemcache($intTopicID, $keyCache, $arrTopic);
            }
        }

        // Return result
        return $arrReturn;
    }

    /**
     * BACKEND: edit $tagID => clear cache cate, dont' del data
     * @param int $tagID
     * @return type 
     */
    public function editTag($tagID)
    {
        try
        {
            $delRS    = false;
            //get key cache
            $keyCache = Fpt_Global::makeCacheKey(self::TAG_DETAIL, $tagID);
            //Delete 1 key cache full only
            $delRS    = Fpt_Global::deleteMemcache($tagID, $keyCache);
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $delRS;
    }

    /**
     * get detail page extend
     * @author TriNT
     * @param <INT> $intArticleID
     * @param <INT> $intPageOrder
     * @return <array>
     */
    public function getDetailPageExtend($intArticleID, $intPageOrder)
    {
        try
        {
            $pageExtend = array();
            $memcacheInstance = Fpt_Global::getMemcacheInstance($intArticleID);
            if ($memcacheInstance !== false)
            {
                $keyCache = Fpt_Global::makeCacheKey(self::PAGE_EXTEND, array($intArticleID, $intPageOrder));
                $pageExtend = $memcacheInstance->read($keyCache);
            }
            // Not in cache?
            if ($pageExtend === FALSE)
            {
                $config    = Fpt_Global::getApplicationIni();
                // Get mysql new instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);

                $pageExtend = $newsMysql->getDetailPageExtend($intArticleID, $intPageOrder);

                if (empty($pageExtend))
                {
                    $pageExtend = array(); //invalid page_extend
                }
                // Store into cache
                $writeCate = Fpt_Global::writeMemcache($intArticleID, $keyCache, $pageExtend);
            }
        }
        catch (Zend_Exception $ex)
        {
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        // Return result
        return $pageExtend;
    }

    /**
     * cronjob check schedule articles => public
     * @return type 
     */
    public function checkScheduleArticles()
    {
        try
        {
            //default return
            $response = false;

            $config        = Fpt_Global::getApplicationIni();
            // Get redis new instance
            $newsNoSql     = Fpt_Business_News::getInstance($config['database']['nosql']);
            $listScheduled = $newsNoSql->getScheduleArticles();
            if (!empty($listScheduled))
            {
                foreach ($listScheduled as $articleID)
                {
                    $response[$articleID] = $this->addArticle($articleID);
                    echo ' Added' . $articleID;
                    var_dump($response[$articleID]);
                }
            }
        }
        catch (Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $response;
    }

    public function getMaxArticle()
    {
        try
        {
            //default response
            $maxID = array();
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $maxID = $newsNosql->getRangeArticle();

            if ($maxID == false)
            {
                //init mysql instance
                $newsMysql = Fpt_Business_News::getInstance($config['database']['default']);
  
                //get db list article by time
                $arrArticleDB = $newsMysql->getRangeArticle();
                //check db
                if ($arrArticleDB != false)
                {
                    $maxID = intval($arrArticleDB['max_id']);
                }//end check get db ok
            }//end check get redis failed?
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $maxID;
    }
    
    public function incrMaxArticle($maxID)
    {
        try
        {
            //default response
            $return    = array();
            // Get application config
            $config    = Fpt_Global::getApplicationIni();
            //get nosql new instance
            $newsNosql = Fpt_Business_News::getInstance($config['database']['nosql']);
            //call redis to get list by time
            $return = $newsNosql->incrRangeArticle($maxID);
        }
        catch (Zend_Exception $ex)
        {
            //log error
            Fpt_Log::sendlog(4, $ex->getCode(), $ex->getMessage(), $ex->getTraceAsString(), null, null);
        }
        return $return;
    }
}